// 第一题

function delay(ms, a, b) {
  var _throw = arguments.length > 2;
  return new Promise(function(res, rej) {
    window.setTimeout(_throw ? rej : res, ms, _throw ? b : a);
  });
}

function concat(a, b) {
  return a.concat(b);
}

function concatP(fn) {
  return function(prev) {
    return fn(prev).then(concat.bind(null, [].concat(prev)));
  }
}

function join(sep, xs) {
  return xs.join(sep);
}

delay(10, 'hello')
.then(concatP(() => delay(10, 'lagou')))
.then(concatP(() => delay(10, 'I ❤️ U')))
.then(join.bind(null, ''))
.then(console.log);

// 第二题, 练习1

var isLastInStock = fp.flowRight(fp.prop('in_stock'), fp.last);

// 第二题, 练习2

var getFirstName = fp.flowRight(fp.prop('name'), fp.first);

// 第二题, 练习3

var averageDollarValue = fp.flowRight(_average, fp.map(fp.propOr(0, 'dollar_value')));

// 第二题, 练习4

var sanitizeNames = fp.map(fp.flowRight(eval.call.bind(String.prototype.toLowerCase), _underscore, fp.propOr('', 'name')));

// 第三题, 练习1

let ex1 = () => {
  var fn = fp.map(fp.add(1));
  return maybe.map(fn);
};

// 第三题, 练习2

let ex2 = () => {
  var fn = fp.first;
  return xs.map(fn);
};

// 第三题, 练习3

let ex3 = () => {
  return safeProp('name', user).map(fp.first);
};

// 第三题, 练习4

let ex4 = (n) => {
  return Maybe.of(n).map(parseInt);
};

// 第四题

const state = (function(base) {
  function once(fn) {
    const cache = {};
    const key = 1;
    return function(...args) {
      if (cache.hasOwnProperty(key)) {
        return cache[key];
      }
      return cache[key] = fn.apply(this, args);
    };
  }
  function zipWith(fn, xs1, xs2) {
    const ret = [];
    const len = Math.min(xs1.length, xs2.length);
    for (let i = 0; i < len; ++i) {
      ret[i] = fn(xs1[i], xs2[i]);
    }
    return ret;
  }
  return function() {
    const ctx = {
      // 0: pending, 1: fulfilled, 2: rejected
      state: 0,
      value: [],
      subscribes: [],
      normalizeSubscribe: zipWith.bind(
        null,
        function(a, b) {
          return typeof b == 'function' ? b : a;
        },
        [null, function(x) {return x;}, function(x) {throw x;}],
      ),
    };
    ctx.receive = once(base.receive.bind(ctx));
    ctx.resolve = ctx.receive.bind(ctx, 1);
    ctx.reject = ctx.receive.bind(ctx, 2);
    ctx.onReceive = base.onReceive.bind(ctx);
    ctx.linker = base.linker.bind(ctx);
    return ctx;
  };
})({
  receive: function(state, next) {
    this.state = state;
    this.value[this.state] = next;
    const fns = this.subscribes.slice();
    this.subscribes.length = 0;
    for (let i = 0, len = fns.length; i < len; ++i) {
      fns[i]();
    }
  },
  onReceive: function(subscribe, relateExecutor) {
    try {
      relateExecutor(subscribe[this.state](this.value[this.state]));
    } catch (e) {
      relateExecutor(null, e);
    }
  },
  linker: function(onFulfilled, onRejected, relateExecutor) {
    const subscribe = this.normalizeSubscribe([null, onFulfilled, onRejected]);
    const _onReceive = this.onReceive.bind(this, subscribe, relateExecutor);
    if (this.state) {
      _onReceive();
    } else {
      this.subscribes.push(_onReceive);
    }
  },
});

class MyPromise {
  constructor(executor) {
    const inner = state();
    this.then = this.then(inner);
    try {
      executor(inner.resolve, inner.reject);
    } catch (e) {
      inner.reject(e);
    }
  }

  then(inner) {
    return function(onFulfilled, onRejected) {
      let ret = new MyPromise(function(resolve, reject) {
        function relExecutor(value, reason) {
          const _throw = arguments.length > 1;
          const val = _throw ? reason : value;
          if (ret === val) {
            reject(new TypeError('Chaining cycle detected for promise #<Promise>'));
          } else {
            try {
              if (val instanceof MyPromise) {
                val.then(resolve, reject);
              } else {
                resolve(val);
              }
            } catch (e) {
              reject(e);
            }
          }
        }
        setTimeout(inner.linker, 0, onFulfilled, onRejected, relExecutor);
      });
      return ret;
    };
  }
}

// 第四题, 测试代码
/*
const log = x => y => (console.log(x, y), y);
const succ = log('succ#');
const fail = log('fail#');

function delay(ms, a, b) {
  const _throw = arguments.length > 2;
  return new MyPromise(function(res, rej) {
    setTimeout(_throw ? rej : res, ms, _throw ? b : a);
  });
}

delay(1000, 23)
.then(log(1), log(12))
.then(() => delay(2000, 45))
.then(log(3), log(32))
.then(succ, fail);
*/